package io.github.chutian0610.sqlparser.token

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe

class CodePointTest : StringSpec({

    "test basic construction and value property" {
        val codePoint = CodePoint('A'.code)
        codePoint.value shouldBe 65
    }

    "test toUnicodeChar for BMP characters" {
        val codePoint = CodePoint('A'.code)
        codePoint.toUnicodeChar() shouldBe "A"
    }

    "test toUnicodeChar for supplementary characters" {
        // U+1F600 (😀)
        val emojiCodePoint = CodePoint(0x1F600)
        emojiCodePoint.toUnicodeChar() shouldBe "😀"
    }

    "test toDigit with default radix" {
        val codePoint0 = CodePoint('0'.code)
        codePoint0.toDigit() shouldBe 0

        val codePoint9 = CodePoint('9'.code)
        codePoint9.toDigit() shouldBe 9

        val codePointA = CodePoint('A'.code)
        codePointA.toDigit() shouldBe null // 默认基数10，'A'不是有效的数字
    }

    "test toDigit with hexadecimal radix" {
        val codePointA = CodePoint('A'.code)
        codePointA.toDigit(16) shouldBe 10

        val codePointF = CodePoint('F'.code)
        codePointF.toDigit(16) shouldBe 15

        val codePointa = CodePoint('a'.code)
        codePointa.toDigit(16) shouldBe 10

        val codePointg = CodePoint('g'.code)
        codePointg.toDigit(16) shouldBe null // 'g'不是有效的十六进制数字
    }

    "test isDigit with default radix" {
        val codePoint0 = CodePoint('0'.code)
        codePoint0.isDigit() shouldBe true

        val codePoint9 = CodePoint('9'.code)
        codePoint9.isDigit() shouldBe true

        val codePointA = CodePoint('A'.code)
        codePointA.isDigit() shouldBe false
    }

    "test isDigit with hexadecimal radix" {
        val codePointA = CodePoint('A'.code)
        codePointA.isDigit(16) shouldBe true

        val codePointF = CodePoint('F'.code)
        codePointF.isDigit(16) shouldBe true

        val codePointa = CodePoint('a'.code)
        codePointa.isDigit(16) shouldBe true

        val codePointg = CodePoint('g'.code)
        codePointg.isDigit(16) shouldBe false
    }

    "test isAsciiDigit" {
        val codePoint0 = CodePoint('0'.code)
        codePoint0.isAsciiDigit() shouldBe true

        val codePoint9 = CodePoint('9'.code)
        codePoint9.isAsciiDigit() shouldBe true

        val codePointA = CodePoint('A'.code)
        codePointA.isAsciiDigit() shouldBe false
    }

    "test isAlphabetic" {
        val codePointA = CodePoint('A'.code)
        codePointA.isAlphabetic() shouldBe true

        val codePointz = CodePoint('z'.code)
        codePointz.isAlphabetic() shouldBe true

        val codePoint0 = CodePoint('0'.code)
        codePoint0.isAlphabetic() shouldBe false

        val codePointSpace = CodePoint(' '.code)
        codePointSpace.isAlphabetic() shouldBe false
    }

    "test isAscii" {
        val codePointA = CodePoint('A'.code)
        codePointA.isAscii() shouldBe true

        val codePointDel = CodePoint(127)
        codePointDel.isAscii() shouldBe true

        val codePointEuro = CodePoint(0x20AC)
        codePointEuro.isAscii() shouldBe false
    }

    "test isAsciiUppercase" {
        val codePointA = CodePoint('A'.code)
        codePointA.isAsciiUppercase() shouldBe true

        val codePointZ = CodePoint('Z'.code)
        codePointZ.isAsciiUppercase() shouldBe true

        val codePointa = CodePoint('a'.code)
        codePointa.isAsciiUppercase() shouldBe false

        val codePoint0 = CodePoint('0'.code)
        codePoint0.isAsciiUppercase() shouldBe false
    }

    "test isAsciiLowercase" {
        val codePointa = CodePoint('a'.code)
        codePointa.isAsciiLowercase() shouldBe true

        val codePointz = CodePoint('z'.code)
        codePointz.isAsciiLowercase() shouldBe true

        val codePointA = CodePoint('A'.code)
        codePointA.isAsciiLowercase() shouldBe false

        val codePoint0 = CodePoint('0'.code)
        codePoint0.isAsciiLowercase() shouldBe false
    }

    "test isAlphanumeric" {
        val codePointA = CodePoint('A'.code)
        codePointA.isAlphanumeric() shouldBe true

        val codePointz = CodePoint('z'.code)
        codePointz.isAlphanumeric() shouldBe true

        val codePoint0 = CodePoint('0'.code)
        codePoint0.isAlphanumeric() shouldBe true

        val codePointSpace = CodePoint(' '.code)
        codePointSpace.isAlphanumeric() shouldBe false
    }

    "test isAsciiHexDigit" {
        val codePoint0 = CodePoint('0'.code)
        codePoint0.isAsciiHexDigit() shouldBe true

        val codePoint9 = CodePoint('9'.code)
        codePoint9.isAsciiHexDigit() shouldBe true

        val codePointA = CodePoint('A'.code)
        codePointA.isAsciiHexDigit() shouldBe true

        val codePointF = CodePoint('F'.code)
        codePointF.isAsciiHexDigit() shouldBe true

        val codePointa = CodePoint('a'.code)
        codePointa.isAsciiHexDigit() shouldBe true

        val codePointf = CodePoint('f'.code)
        codePointf.isAsciiHexDigit() shouldBe true

        val codePointG = CodePoint('G'.code)
        codePointG.isAsciiHexDigit() shouldBe false

        val codePointSpace = CodePoint(' '.code)
        codePointSpace.isAsciiHexDigit() shouldBe false
    }

    "test isWhitespace" {
        val codePointSpace = CodePoint(' '.code)
        codePointSpace.isWhitespace() shouldBe true

        val codePointTab = CodePoint('\t'.code)
        codePointTab.isWhitespace() shouldBe true

        val codePointNewline = CodePoint('\n'.code)
        codePointNewline.isWhitespace() shouldBe true

        val codePointA = CodePoint('A'.code)
        codePointA.isWhitespace() shouldBe false
    }

    "test isNumeric" {
        val codePoint0 = CodePoint('0'.code)
        codePoint0.isNumeric() shouldBe true

        val codePoint9 = CodePoint('9'.code)
        codePoint9.isNumeric() shouldBe true

        // Unicode 上标数字 ² (U+00B2)
        val codePointSuperscript2 = CodePoint(0x00B2)
        codePointSuperscript2.isNumeric() shouldBe true

        val codePointA = CodePoint('A'.code)
        codePointA.isNumeric() shouldBe false
    }

    // 伴生对象方法测试

    "test fromChar" {
        val codePoint = CodePoint.fromChar('A')
        codePoint.value shouldBe 65
    }

    "test fromInt" {
        val codePoint = CodePoint.fromInt(65)
        codePoint.value shouldBe 65
    }

    fun `test equalsChar`() {
        val codePointA = CodePoint('A'.code)
        CodePoint.equalsChar(codePointA, 'A') shouldBe true
        CodePoint.equalsChar(codePointA, 'B') shouldBe false
        CodePoint.equalsChar(null, 'A') shouldBe false
    }

    "test fromString" {
        val codePoint = CodePoint.fromString("ABC")
        codePoint.value shouldBe 'A'.code
    }

    "test inCharArray" {
        val codePointA = CodePoint('A'.code)
        CodePoint.inCharArray(codePointA, 'A', 'B', 'C') shouldBe true
        CodePoint.inCharArray(codePointA, 'X', 'Y', 'Z') shouldBe false
        CodePoint.inCharArray(null, 'A', 'B', 'C') shouldBe false
    }

    "test inCharRange" {
        val codePointA = CodePoint('A'.code)
        CodePoint.inCharRange(codePointA, 'A'..'Z') shouldBe true
        CodePoint.inCharRange(codePointA, 'a'..'z') shouldBe false
        CodePoint.inCharRange(null, 'A'..'Z') shouldBe false
    }

    "test inIntRange" {
        val codePointA = CodePoint('A'.code)
        CodePoint.inIntRange(codePointA, 65..90) shouldBe true
        CodePoint.inIntRange(codePointA, 97..122) shouldBe false
    }
})